package com.hw.nio.http;


import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class SimpleHttpServer {

    int cupCores = Runtime.getRuntime().availableProcessors();
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(cupCores * 2, cupCores * 3, 500, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
    private HttpServlet httpServlet;
    private Integer port;


    public SimpleHttpServer(HttpServlet httpServlet, Integer port) {
        this.httpServlet = httpServlet;
        this.port = port;
    }

    public Thread start() {

        Thread thread = new Thread(() -> {
            try {
                dispatch();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        thread.start();

        return thread;
    }

    /**
     * 实现思路：
     * 1、打开socketServerChannel，绑定端口号
     * 2、注册selector 监听accept事件 ，等待连接
     * 3、获取连接事件后获取socketChannel 注册读事件
     * 3、连接来了以后，先解码，解码后交给servlet去执行
     * 4、servlet需要异步执行
     * 4、执行完毕后获取响应，
     * 5、将响应编码返回给客户端
     */
    public void dispatch() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (true) {
            selector.select(2000);
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = channel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    // 编码
                    final ByteOutputStream byteOutputStream = new ByteOutputStream();
                    final SocketChannel channel = (SocketChannel) selectionKey.channel();
                    while (channel.read(byteBuffer) > 0) {
                        byteBuffer.flip();
                        byteOutputStream.write(byteBuffer.array(), 0, byteBuffer.limit());
                        byteBuffer.clear();
                    }
                    if (byteOutputStream.size() == 0) {
                        continue;
                    }


                    threadPoolExecutor.submit(() -> {
                        Request request = decode(byteOutputStream.getBytes());
                        Response response = new Response();
                        if (request.getMethod().equalsIgnoreCase("get")) {
                            httpServlet.doGet(request, response);
                        } else {
                            httpServlet.doPost(request, response);
                        }
                        try {
                            channel.write(ByteBuffer.wrap(encode(response)));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
                }
            }
        }
    }

    //编码Http 服务
    private byte[] encode(Response response) {
        StringBuilder builder = new StringBuilder(512);
        builder.append("HTTP/1.1 ")
                .append(response.code).append(Code.msg(response.code)).append("\r\n");

        if (response.body != null && response.body.length() != 0) {
            builder.append("Content-Length: ")
                    .append(response.body.getBytes().length).append("\r\n")
                    .append("Content-Type: text/html\r\n");
        }
        if (response.headers != null) {
            String headStr = response.getHeaders().entrySet().stream().map(e -> e.getKey() + ":" + e.getValue())
                    .collect(Collectors.joining("\r\n"));
            builder.append(headStr + "\r\n");
        }


//      builder.append ("Connection: close\r\n");// 执行完后关闭链接
        builder.append("\r\n").append(response.body);
        return builder.toString().getBytes(StandardCharsets.UTF_8);
    }


    private Request decode(byte[] requestBytes) {

        Request request = new Request();
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(requestBytes)));
            String firstLine = bufferedReader.readLine();

            String[] firseLineArr = firstLine.split(" ");
            request.setMethod(firseLineArr[0]);
            request.setUrl(firseLineArr[1]);
            request.setVersion(firseLineArr[2]);
            Map<String, String> map = new HashMap<>();
            while (true) {
                String line = bufferedReader.readLine();
                if (line != null && line.trim().equals("")) {
                    line = bufferedReader.readLine();
                    request.setRequestBody(line);
                    request.setHeaders(map);
                    break;
                }
                String[] header = line.split(": ");
                if (header.length > 1) {
                    map.put(header[0], header[1]);
                } else {
                    System.out.println(header);
                }

            }
//            System.out.println(new String(requestInfo, StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return request;
    }

}
